{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "acffb12e-6207-429f-bf1a-94e8427dedb0",
   "metadata": {},
   "source": [
    "# 1、获取仓库下的tag列表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00308818-7155-4727-a1c2-8829e7d3f3f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "import requests\n",
    "\n",
    "url = \"https://api.atomgit.com/repos/atomgit_operate/feedback/tags\"\n",
    "\n",
    "payload={\"per_page\": \"10\", \"page\":\"1\", \"search\":\"\"}\n",
    "headers = {\"Authorization\": \"Bearer atp_4srjqtlktpfjqy812n622hl8ohgy47kb\"}\n",
    "\n",
    "response = requests.request(\"GET\", url, headers=headers, data=payload)\n",
    "\n",
    "print(response.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4762e42b-0559-4341-82d8-16a9c9584be7",
   "metadata": {},
   "source": [
    "#  2、获取仓库下的issue的所有标签"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f3660e3-f92c-4a1b-bdfb-8a341cf0d222",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "import requests\n",
    "\n",
    "url = \"https://api.atomgit.com/repos/atomgit_operate/feedback/issues/196/labels\"\n",
    "\n",
    "payload={}\n",
    "headers = {\n",
    "  'Accept': 'application/json',\n",
    "  \"Authorization\": \"Bearer atp_4srjqtlktpfjqy812n622hl8ohgy47kb\"\n",
    "}\n",
    "\n",
    "response = requests.request(\"GET\", url, headers=headers, data=payload)\n",
    "\n",
    "print(response.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be925542-97b0-4dc1-9e7c-60110de99121",
   "metadata": {},
   "source": [
    "# 3、获取特定仓库的所有 Issues"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ba82925-080c-489c-9e98-7dbcb0a034af",
   "metadata": {},
   "outputs": [],
   "source": [
    "pip install pytz jinja2 markdown"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7bbadfa4-923e-47fa-86b1-b9cf9f12feb5",
   "metadata": {},
   "source": [
    "### 一些常量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7dc40a2b-1b37-4134-bb85-0afecbbae5bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# token = \"atp_4srjqtlktpfjqy812n622hl8ohgy47kb\"  # 替换为你的访问令牌\n",
    "# 调用前端接口方式\n",
    "token = \"ATOMGIT_ID_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI2NTcwOTJkNmU4OTA2Y2QzZDIzY2MyZDIiLCJhdWQiOiI2MmQ3YTBlYmFmOTViZGFjZjc0ZmM2YjkiLCJpYXQiOjE3MTUzMzczMTMsImV4cCI6MTcxNjU0NjkxMywiaXNzIjoiaHR0cHM6Ly9wYXNzcG9ydC5hdG9tZ2l0LmNvbS9vaWRjIiwibm9uY2UiOiJGT1A2Tm50eEl6IiwibmFtZSI6bnVsbCwiZ2l2ZW5fbmFtZSI6bnVsbCwibWlkZGxlX25hbWUiOm51bGwsImZhbWlseV9uYW1lIjpudWxsLCJuaWNrbmFtZSI6Imt5bGUiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOm51bGwsInByb2ZpbGUiOiIiLCJwaWN0dXJlIjoiLy9zdGF0aWMuYXRvbWdpdC5jb20vc3RhdGljcy9hdXRoaW5nLWNvbnNvbGUvL3VwbG9hZHMvdXNlci8xNzA2NzcwNDYwOTM4XzQwODcuanBlZyIsIndlYnNpdGUiOiJodHRwczovL2F0b21naXQuY29tL2t5bGUiLCJiaXJ0aGRhdGUiOm51bGwsImdlbmRlciI6IlUiLCJ6b25laW5mbyI6bnVsbCwibG9jYWxlIjpudWxsLCJ1cGRhdGVkX2F0IjoiMjAyNC0wNS0wOVQwNzowNzozNi4zMzlaIiwiZW1haWwiOiJzdWNjcGVraW5nQGZveG1haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInBob25lX251bWJlciI6IjEzNjkxNDkwNTY4IiwicGhvbmVfbnVtYmVyX3ZlcmlmaWVkIjp0cnVlfQ.Tz-GLg1d4Pz81UYlFUHqCAS96RUgBqcWCt4JQr_kKEM; ATOMGIT_ACCESS_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlBTRkpTbFhZTGphbFA4RGZpUTZITk53TkFXM0FkT1dscTFKaVpTMmlkbFUifQ.eyJzdWIiOiI2NTcwOTJkNmU4OTA2Y2QzZDIzY2MyZDIiLCJhdWQiOiI2MmQ3YTBlYmFmOTViZGFjZjc0ZmM2YjkiLCJzY29wZSI6InByb2ZpbGUgZW1haWwgcGhvbmUgb3BlbmlkIG9mZmxpbmVfYWNjZXNzIiwiaWF0IjoxNzE1MzM3MzEzLCJleHAiOjE3MTY1NDY5MTMsImp0aSI6InJCUWw0NWppazVQYnhEYlNwMzVSZ21hYVlNM3A5T2VUZE5OTUg0YXBmT0kiLCJpc3MiOiJodHRwczovL3Bhc3Nwb3J0LmF0b21naXQuY29tL29pZGMifQ.Oj-ngvJi_G3rH0-ImJogJdSlsU1waVVm3pDYXV_HrmNYUP8A4lqorkKtM6zy-lj9LY3bif0cI5QLxW3fgUArsifD8CbjkNKr_spD_F_BePZpB6RH6GCl5EfN2_9dt8faWhVyqssZDoZ3zS-pisfxi9UmIUuXhT24fUVdFA2NPFXUorX9JexsnT_ZRi5PMRKtisuLfcPvUpP021bE9Ci98IOIDug6bvcCa3VCJS9Oy0wauYnrDn6wV6RWquMSQBRC5-_qiEd2ReDKbjy3qg9BaFIjJ_xNUwiJRaJpJtTF3o2nsMANZ-TBNGbCi1xFbtKsZFbs0m15RsAEMOXEs-PmBg; ATOMGIT_REFRESH_TOKEN=quY30sQjaE4P6FiVaDrOrmhrZYijsrN18TU_2vyxTsd; ATOMGIT_EXPIRES_IN=1209600; ATOMGIT_EXPIRES_AT=1716546913; DEFAULT_LANG=zh_CN; atom_user={%22id%22:%22657092d6e8906cd3d23cc2d2%22%2C%22username%22:%22kyle%22%2C%22nickname%22:%22kyle%22%2C%22photo%22:%22/uploads/user/1706770460938_4087.jpeg%22%2C%22userNameSpace%22:%22kyle%22%2C%22phone%22:%2213691490568%22%2C%22phoneVerified%22:true%2C%22email%22:%22succpeking@foxmail.com%22%2C%22emailVerified%22:true%2C%22adminRoleCode%22:%22Admin%22}; saiti_access_info={%22sourceId%22:null%2C%22website%22:%22https://atomgit.com/kyle%22%2C%22noReadMsg%22:0%2C%22address%22:%22%E5%8C%97%E4%BA%AC%22%2C%22gender%22:%22U%22%2C%22profile%22:%22%22%2C%22photo%22:%22/uploads/user/1706770460938_4087.jpeg%22%2C%22updateTime%22:1715838182769%2C%22token%22:%22eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlBTRkpTbFhZTGphbFA4RGZpUTZITk53TkFXM0FkT1dscTFKaVpTMmlkbFUifQ.eyJzdWIiOiI2NTcwOTJkNmU4OTA2Y2QzZDIzY2MyZDIiLCJhdWQiOiI2MmQ3YTBlYmFmOTViZGFjZjc0ZmM2YjkiLCJzY29wZSI6InByb2ZpbGUgZW1haWwgcGhvbmUgb3BlbmlkIG9mZmxpbmVfYWNjZXNzIiwiaWF0IjoxNzE1MzM3MzEzLCJleHAiOjE3MTY1NDY5MTMsImp0aSI6InJCUWw0NWppazVQYnhEYlNwMzVSZ21hYVlNM3A5T2VUZE5OTUg0YXBmT0kiLCJpc3MiOiJodHRwczovL3Bhc3Nwb3J0LmF0b21naXQuY29tL29pZGMifQ.Oj-ngvJi_G3rH0-ImJogJdSlsU1waVVm3pDYXV_HrmNYUP8A4lqorkKtM6zy-lj9LY3bif0cI5QLxW3fgUArsifD8CbjkNKr_spD_F_BePZpB6RH6GCl5EfN2_9dt8faWhVyqssZDoZ3zS-pisfxi9UmIUuXhT24fUVdFA2NPFXUorX9JexsnT_ZRi5PMRKtisuLfcPvUpP021bE9Ci98IOIDug6bvcCa3VCJS9Oy0wauYnrDn6wV6RWquMSQBRC5-_qiEd2ReDKbjy3qg9BaFIjJ_xNUwiJRaJpJtTF3o2nsMANZ-TBNGbCi1xFbtKsZFbs0m15RsAEMOXEs-PmBg%22%2C%22expiresIn%22:1209600%2C%22isAgree%22:%221%22%2C%22createBy%22:null%2C%22phone%22:%2213691490568%22%2C%22createTime%22:null%2C%22updateBy%22:%22tianyiman%22%2C%22name%22:null%2C%22nickname%22:%22kyle%22%2C%22company%22:%22Openatom%22%2C%22id%22:null%2C%22email%22:%22succpeking@foxmail.com%22%2C%22username%22:%22kyle%22%2C%22status%22:%221%22}; Hm_lvt_3aae7019e3354d0d43daf0842ea3d4b2=1715324737,1715389252,1715772515,1716168210; ak_user_locale=zh_CN; teambition_lang=zh_CN; acw_tc=0b6e703e17161728159086713e1febfacd63a1df6bc72852a7371bf0b9c87b; Hm_lpvt_3aae7019e3354d0d43daf0842ea3d4b2=1716174146\"  # 替换为你的访问令牌"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5967e4c5-a0eb-4592-b5bc-bb484dd1c2af",
   "metadata": {},
   "source": [
    "## 3.1、日报"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "17bb6248-a211-4382-8cf3-5621618e3915",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "md，html 日报导出成功。\n"
     ]
    }
   ],
   "source": [
    "import requests\n",
    "from datetime import datetime, timedelta\n",
    "import pytz\n",
    "from collections import defaultdict\n",
    "import markdown  # 导入 markdown 库\n",
    "\n",
    "# API 参数\n",
    "project_id = \"25287444\"\n",
    "token = \"your_token_here\"  # 替换为你的访问令牌\n",
    "leader_email = \"leader@atomgit.com\"  # 领导的邮箱\n",
    "\n",
    "# 获取 Issue 数据\n",
    "def get_issues(project_id, status, page_num=1, page_size=1000):\n",
    "    url = f\"https://atomgit.com/api/issue/v1/page?projectId={project_id}&status={status}&order=recent_update&pageNum={page_num}&pageSize={page_size}\"\n",
    "    headers = {\n",
    "        'Accept': 'application/json, text/plain, */*',\n",
    "        \"Cookie\": f\"ATOMGIT_ID_TOKEN={token}\"\n",
    "    }\n",
    "    try:\n",
    "        response = requests.get(url, headers=headers)\n",
    "        response.raise_for_status()\n",
    "        return response.json()\n",
    "    except requests.exceptions.HTTPError as http_err:\n",
    "        print(f\"HTTP error occurred: {http_err}\")\n",
    "    except Exception as err:\n",
    "        print(f\"Other error occurred: {err}\")\n",
    "    return None\n",
    "\n",
    "# 获取所有 Issue 数据\n",
    "def get_all_issues(status=\"\"):\n",
    "    response = get_issues(project_id, status)\n",
    "    if response and 'content' in response:\n",
    "        return response['content']\n",
    "    else:\n",
    "        print(f\"Failed to fetch issues: {response['message'] if response else 'No response'}\")\n",
    "    return []\n",
    "\n",
    "# 计算统计数据\n",
    "def calculate_statistics(issues, start_date=None):\n",
    "    if start_date:\n",
    "        issues = [issue for issue in issues if datetime.fromisoformat(issue['createdDate'].replace(\"Z\", \"+00:00\")) >= start_date]\n",
    "    \n",
    "    stats = {\n",
    "        'all': len(issues),\n",
    "        'open': sum(1 for issue in issues if issue['status'] == 1),\n",
    "        'closed': sum(1 for issue in issues if issue['status'] == 2),\n",
    "    }\n",
    "    return stats\n",
    "\n",
    "# 生成日报数据\n",
    "def generate_report_data():\n",
    "    all_issues = get_all_issues(\"\")\n",
    "    now = datetime.now(tz=pytz.UTC)\n",
    "    start_of_day = now - timedelta(days=1)\n",
    "    start_of_month = now.replace(day=1)\n",
    "\n",
    "    overall_stats = calculate_statistics(all_issues)\n",
    "    daily_stats = calculate_statistics(all_issues, start_of_day)\n",
    "    monthly_stats = calculate_statistics(all_issues, start_of_month)\n",
    "\n",
    "    open_issues_by_assignee = defaultdict(lambda: {'issues': [], 'stats': {}})\n",
    "    for issue in all_issues:\n",
    "        assignee_info = issue.get('assignUserInfo', {})\n",
    "        assignee_username = assignee_info.get('username', '未分配')\n",
    "        assignee_nickname = assignee_info.get('nickname', '未知')\n",
    "        assignee_email = assignee_info.get('email', 'unknown@example.com')\n",
    "\n",
    "        open_issues_by_assignee[assignee_username]['username'] = assignee_username\n",
    "        open_issues_by_assignee[assignee_username]['nickname'] = assignee_nickname\n",
    "        open_issues_by_assignee[assignee_username]['email'] = assignee_email\n",
    "        open_issues_by_assignee[assignee_username]['issues'].append(issue)\n",
    "    \n",
    "    for assignee, info in open_issues_by_assignee.items():\n",
    "        info['stats']['overall'] = calculate_statistics(info['issues'])\n",
    "        info['stats']['monthly'] = calculate_statistics(info['issues'], start_of_month)\n",
    "        info['stats']['daily'] = calculate_statistics(info['issues'], start_of_day)\n",
    "\n",
    "    return {\n",
    "        \"overall_stats\": overall_stats,\n",
    "        \"daily_stats\": daily_stats,\n",
    "        \"monthly_stats\": monthly_stats,\n",
    "        \"open_issues_by_assignee\": open_issues_by_assignee,\n",
    "    }\n",
    "\n",
    "def format_datetime(datetime_str):\n",
    "    dt = datetime.fromisoformat(datetime_str.replace(\"Z\", \"+00:00\"))\n",
    "    return dt.strftime('%Y-%m-%d %H:%M:%S')\n",
    "\n",
    "# 生成日报内容 (Markdown 格式)\n",
    "def generate_report(assignee_info, issues):\n",
    "    def issue_sort_key(issue):\n",
    "        return issue['createdDate']\n",
    "    \n",
    "    open_issues = [issue for issue in issues if issue['status'] == 1]\n",
    "    sorted_issues = sorted(open_issues, key=issue_sort_key)\n",
    "    \n",
    "    report = []\n",
    "    report.append(f\"\\n## 😉{assignee_info['username']}/{assignee_info['nickname']}\")\n",
    "    report.append(\"#### 未解决/未关闭 Issues\\n\")\n",
    "    report.append(\"| 负责人 | 标签 | 标题 | 创建时间 | URL |\")\n",
    "    report.append(\"| --- | --- | --- | --- | --- |\")\n",
    "    \n",
    "    for issue in sorted_issues:\n",
    "        assignee = f\"{assignee_info['username']}/{assignee_info['nickname']}\"\n",
    "        labels = \", \".join(label['labelName'] for label in issue.get('labels', []))\n",
    "        title = issue['title']\n",
    "        created_at = format_datetime(issue['createdDate'])\n",
    "        url = f\"https://atomgit.com/{issue['pathWithNamespace'].replace(' ', '')}/issues/{issue['serialNumber']}\"\n",
    "        report.append(f\"| {assignee} | {labels} | {title} | {created_at} | [#{issue['serialNumber']}]({url}) |\")\n",
    "    \n",
    "    return \"\\n\".join(report)\n",
    "\n",
    "def generate_full_report():\n",
    "    report_data = generate_report_data()\n",
    "    if report_data:\n",
    "        current_date = datetime.now().date()\n",
    "        start_of_day = datetime.now(tz=pytz.UTC) - timedelta(days=1)\n",
    "        day_number = current_date.strftime('%Y%m%d')\n",
    "        \n",
    "        all_assignees_email = set()\n",
    "        markdown_report_sections = []\n",
    "        assignees_summary_sections = []\n",
    "\n",
    "        for assignee, info in sorted(report_data['open_issues_by_assignee'].items(), key=lambda item: item[1]['stats']['overall']['open'], reverse=True):\n",
    "            markdown_report_sections.append(generate_report(info, info['issues']))\n",
    "            assignees_summary_sections.append(f\"| {info['username']} | {info['nickname']} | {info['stats']['overall']['all']} | {info['stats']['overall']['open']} | {info['stats']['overall']['closed']} |\")\n",
    "            all_assignees_email.add(info['email'])\n",
    "        \n",
    "        assignees_summary = \"\\n\".join(assignees_summary_sections)\n",
    "        summary = f\"\"\"\n",
    "Hi, All\n",
    "\n",
    "最新的 Issues 情况，请对应的 Assignee 对应支持下，感谢~\n",
    "\n",
    "---\n",
    "\n",
    "**注:** \n",
    "**1、未关闭的问题不一定代表未解决。**\n",
    "**2、如果负责人指派不合适，请与管理员联系或者自己修改到合适的 Assignee。**\n",
    "**3、请对应 Issue 的 assignee 支持下。收集用户真实的反馈不易，还请理解。谢谢~**\n",
    "\n",
    "# 📊总 Issue 数据\n",
    "- **总问题数（全部）:** {report_data['overall_stats']['all']}\n",
    "  - **未解决/未关闭的问题:** {report_data['overall_stats']['open']}\n",
    "  - **已关闭的问题:** {report_data['overall_stats']['closed']}\n",
    "  \n",
    "- **本月问题数（全部）:** {report_data['monthly_stats']['all']}\n",
    "  - **未解决/未关闭的问题:** {report_data['monthly_stats']['open']}\n",
    "  - **已关闭的问题:** {report_data['monthly_stats']['closed']}\n",
    "  \n",
    "- **昨日问题数（全部）:** {report_data['daily_stats']['all']}\n",
    "  - **未解决/未关闭的问题:** {report_data['daily_stats']['open']}\n",
    "  - **已关闭的问题:** {report_data['daily_stats']['closed']}\n",
    "\n",
    "---\n",
    "\n",
    "# 😎各负责人汇总\n",
    "| 负责人 | 昵称 | 总问题数 | 未解决/未关闭 | 已关闭 |\n",
    "| --- | --- | --- | --- | --- |\n",
    "{assignees_summary}\n",
    "\n",
    "---\n",
    "\n",
    "# 😎各负责人未解决/未关闭列表\n",
    "\"\"\"\n",
    "        markdown_report = summary + \"\\n\".join(markdown_report_sections)\n",
    "\n",
    "        email_base = f\"\"\"\n",
    "> ----[start]------\n",
    "> subject: AtomGit 用户反馈整理及对应支持 | {day_number}\n",
    "> to: {', '.join(all_assignees_email)}\n",
    "> cc: {leader_email}\n",
    "> ----[end]------\n",
    "\"\"\"\n",
    "\n",
    "        # 使用 markdown 库将 markdown 转换为 html\n",
    "        html_content = markdown.markdown(markdown_report, extensions=['extra', 'tables'])\n",
    "\n",
    "        full_report_html = (\n",
    "            f\"<html>\\n<head>\\n\"\n",
    "            f\"    <title>用户反馈日报 ({day_number})</title>\\n\"\n",
    "            f\"    <style>\\n\"\n",
    "            f\"        table, th, td {{\\n\"\n",
    "            f\"            border: 1px solid black;\\n\"\n",
    "            f\"            border-collapse: collapse;\\n\"\n",
    "            f\"        }}\\n\"\n",
    "            f\"        th, td {{\\n\"\n",
    "            f\"            padding: 8px;\\n\"\n",
    "            f\"        }}\\n\"\n",
    "            f\"        th {{\\n\"\n",
    "            f\"            background-color: #f2f2f2;\\n\"\n",
    "            f\"        }}\\n\"\n",
    "            f\"    </style>\\n\"\n",
    "            f\"</head>\\n<body>\\n\"\n",
    "            f\"{html_content}\\n\"\n",
    "            f\"</body>\\n</html>\"\n",
    "        )\n",
    "\n",
    "        with open(f\"daily_report_{day_number}.md\", \"w\", encoding=\"utf-8\") as file_md:\n",
    "            file_md.write(email_base + markdown_report)\n",
    "\n",
    "        with open(f\"daily_report_{day_number}.html\", \"w\", encoding=\"utf-8\") as file_html:\n",
    "            file_html.write(full_report_html)\n",
    "\n",
    "        print(\"md，html 日报导出成功。\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    generate_full_report()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdb271c6-d927-4e98-9ef8-dd4fd2efda58",
   "metadata": {},
   "source": [
    "## 3.2、周报"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6506f2ae-fd25-47ec-8a74-eb74a7dbcab0",
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "from datetime import datetime, timedelta\n",
    "import pytz\n",
    "from collections import defaultdict\n",
    "import markdown  # 导入 markdown 库\n",
    "\n",
    "# API 参数\n",
    "project_id = \"25287444\"\n",
    "token = \"your_token_here\"  # 替换为你的访问令牌\n",
    "leader_email = \"leader@atomgit.com\"  # 领导的邮箱\n",
    "\n",
    "# 获取 Issue 数据\n",
    "def get_issues(project_id, status, page_num=1, page_size=1000):\n",
    "    url = f\"https://atomgit.com/api/issue/v1/page?projectId={project_id}&status={status}&order=recent_update&pageNum={page_num}&pageSize={page_size}\"\n",
    "    headers = {\n",
    "        'Accept': 'application/json, text/plain, */*',\n",
    "        \"Cookie\": f\"ATOMGIT_ID_TOKEN={token}\"\n",
    "    }\n",
    "    try:\n",
    "        response = requests.get(url, headers=headers)\n",
    "        response.raise_for_status()\n",
    "        return response.json()\n",
    "    except requests.exceptions.HTTPError as http_err:\n",
    "        print(f\"HTTP error occurred: {http_err}\")\n",
    "    except Exception as err:\n",
    "        print(f\"Other error occurred: {err}\")\n",
    "    return None\n",
    "\n",
    "# 获取所有 Issue 数据\n",
    "def get_all_issues(status=\"\"):\n",
    "    response = get_issues(project_id, status)\n",
    "    if response and 'content' in response:\n",
    "        return response['content']\n",
    "    else:\n",
    "        print(f\"Failed to fetch issues: {response['message'] if response else 'No response'}\")\n",
    "    return []\n",
    "\n",
    "# 计算统计数据\n",
    "def calculate_statistics(issues, start_date=None):\n",
    "    if start_date:\n",
    "        issues = [issue for issue in issues if datetime.fromisoformat(issue['createdDate'].replace(\"Z\", \"+00:00\")) >= start_date]\n",
    "    \n",
    "    stats = {\n",
    "        'all': len(issues),\n",
    "        'open': sum(1 for issue in issues if issue['status'] == 1),\n",
    "        'closed': sum(1 for issue in issues if issue['status'] == 2),\n",
    "    }\n",
    "    return stats\n",
    "\n",
    "# 生成周报数据\n",
    "def generate_report_data():\n",
    "    all_issues = get_all_issues(\"\")\n",
    "    now = datetime.now(tz=pytz.UTC)\n",
    "    start_of_week = now - timedelta(days=now.weekday())\n",
    "    start_of_month = now.replace(day=1)\n",
    "\n",
    "    overall_stats = calculate_statistics(all_issues)\n",
    "    weekly_stats = calculate_statistics(all_issues, start_of_week)\n",
    "    monthly_stats = calculate_statistics(all_issues, start_of_month)\n",
    "\n",
    "    open_issues_by_assignee = defaultdict(lambda: {'issues': [], 'stats': {}})\n",
    "    for issue in all_issues:\n",
    "        assignee_info = issue.get('assignUserInfo', {})\n",
    "        assignee_username = assignee_info.get('username', '未分配')\n",
    "        assignee_nickname = assignee_info.get('nickname', '未知')\n",
    "        assignee_email = assignee_info.get('email', 'unknown@example.com')\n",
    "\n",
    "        open_issues_by_assignee[assignee_username]['username'] = assignee_username\n",
    "        open_issues_by_assignee[assignee_username]['nickname'] = assignee_nickname\n",
    "        open_issues_by_assignee[assignee_username]['email'] = assignee_email\n",
    "        open_issues_by_assignee[assignee_username]['issues'].append(issue)\n",
    "    \n",
    "    for assignee, info in open_issues_by_assignee.items():\n",
    "        info['stats']['overall'] = calculate_statistics(info['issues'])\n",
    "        info['stats']['monthly'] = calculate_statistics(info['issues'], start_of_month)\n",
    "        info['stats']['weekly'] = calculate_statistics(info['issues'], start_of_week)\n",
    "\n",
    "    return {\n",
    "        \"overall_stats\": overall_stats,\n",
    "        \"weekly_stats\": weekly_stats,\n",
    "        \"monthly_stats\": monthly_stats,\n",
    "        \"open_issues_by_assignee\": open_issues_by_assignee,\n",
    "    }\n",
    "\n",
    "def format_datetime(datetime_str):\n",
    "    dt = datetime.fromisoformat(datetime_str.replace(\"Z\", \"+00:00\"))\n",
    "    return dt.strftime('%Y-%m-%d %H:%M:%S')\n",
    "\n",
    "# 生成周报内容 (Markdown 格式)\n",
    "def generate_report(assignee_info, issues):\n",
    "    def issue_sort_key(issue):\n",
    "        return issue['createdDate']\n",
    "    \n",
    "    open_issues = [issue for issue in issues if issue['status'] == 1]\n",
    "    sorted_issues = sorted(open_issues, key=issue_sort_key)\n",
    "    \n",
    "    report = []\n",
    "    report.append(f\"\\n## 😉{assignee_info['username']}/{assignee_info['nickname']}\")\n",
    "    report.append(\"#### 未解决/未关闭 Issues\\n\")\n",
    "    report.append(\"| 负责人 | 标签 | 标题 | 创建时间 | URL |\")\n",
    "    report.append(\"| --- | --- | --- | --- | --- |\")\n",
    "    \n",
    "    for issue in sorted_issues:\n",
    "        assignee = f\"{assignee_info['username']}/{assignee_info['nickname']}\"\n",
    "        labels = \", \".join(label['labelName'] for label in issue.get('labels', []))\n",
    "        title = issue['title']\n",
    "        created_at = format_datetime(issue['createdDate'])\n",
    "        url = f\"https://atomgit.com/{issue['pathWithNamespace'].replace(' ', '')}/issues/{issue['serialNumber']}\"\n",
    "        report.append(f\"| {assignee} | {labels} | {title} | {created_at} | [#{issue['serialNumber']}]({url}) |\")\n",
    "    \n",
    "    return \"\\n\".join(report)\n",
    "\n",
    "def generate_full_report():\n",
    "    report_data = generate_report_data()\n",
    "    if report_data:\n",
    "        current_date = datetime.now().date()\n",
    "        start_of_week = current_date - timedelta(days=current_date.weekday())\n",
    "        end_of_week = start_of_week + timedelta(days=6)\n",
    "        week_number = current_date.isocalendar()[1]\n",
    "        \n",
    "        all_assignees_email = set()\n",
    "        markdown_report_sections = []\n",
    "        assignees_summary_sections = []\n",
    "\n",
    "        for assignee, info in sorted(report_data['open_issues_by_assignee'].items(), key=lambda item: item[1]['stats']['overall']['open'], reverse=True):\n",
    "            markdown_report_sections.append(generate_report(info, info['issues']))\n",
    "            assignees_summary_sections.append(f\"| {info['username']} | {info['nickname']} | {info['stats']['overall']['all']} | {info['stats']['overall']['open']} | {info['stats']['overall']['closed']} |\")\n",
    "            all_assignees_email.add(info['email'])\n",
    "        \n",
    "        assignees_summary = \"\\n\".join(assignees_summary_sections)\n",
    "        summary = f\"\"\"\n",
    "Hi, All\n",
    "\n",
    "最新的 Issues 周报情况，请对应的 Assignee 对应支持下，感谢~\n",
    "\n",
    "---\n",
    "\n",
    "**注:** \n",
    "**1、未关闭的问题不一定代表未解决。**\n",
    "**2、如果负责人指派不合适，请与管理员联系或者自己修改到合适的 Assignee。**\n",
    "**3、请对应 Issue 的 assignee 支持下。收集用户真实的反馈不易，还请理解。谢谢~**\n",
    "\n",
    "# 📊总 Issue 数据\n",
    "- **总问题数（全部）:** {report_data['overall_stats']['all']}\n",
    "  - **未解决/未关闭的问题:** {report_data['overall_stats']['open']}\n",
    "  - **已关闭的问题:** {report_data['overall_stats']['closed']}\n",
    "  \n",
    "- **本月问题数（全部）:** {report_data['monthly_stats']['all']}\n",
    "  - **未解决/未关闭的问题:** {report_data['monthly_stats']['open']}\n",
    "  - **已关闭的问题:** {report_data['monthly_stats']['closed']}\n",
    "  \n",
    "- **本周问题数（全部）:** {report_data['weekly_stats']['all']}\n",
    "  - **未解决/未关闭的问题:** {report_data['weekly_stats']['open']}\n",
    "  - **已关闭的问题:** {report_data['weekly_stats']['closed']}\n",
    "\n",
    "---\n",
    "\n",
    "# 😎各负责人汇总\n",
    "| 负责人 | 昵称 | 总问题数 | 未解决/未关闭 | 已关闭 |\n",
    "| --- | --- | --- | --- | --- |\n",
    "{assignees_summary}\n",
    "\n",
    "---\n",
    "\n",
    "# 😎各负责人未解决/未关闭列表\n",
    "\"\"\"\n",
    "        markdown_report = summary + \"\\n\".join(markdown_report_sections)\n",
    "\n",
    "        email_base = f\"\"\"\n",
    "> ----[start]------\n",
    "> subject: w{week_number}|{start_of_week.strftime('%Y%m%d')}-{end_of_week.strftime('%Y%m%d')}|用户反馈周报\n",
    "> to: {', '.join(all_assignees_email)}\n",
    "> cc: {leader_email}\n",
    "> ----[end]------\n",
    "\"\"\"\n",
    "\n",
    "        # 使用 markdown 库将 markdown 转换为 html\n",
    "        html_content = markdown.markdown(markdown_report, extensions=['extra', 'tables'])\n",
    "\n",
    "        full_report_html = (\n",
    "            f\"<html>\\n<head>\\n\"\n",
    "            f\"    <title>用户反馈周报 (w{week_number} {start_of_week.strftime('%Y%m%d')} - {end_of_week.strftime('%Y%m%d')})</title>\\n\"\n",
    "            f\"    <style>\\n\"\n",
    "            f\"        table, th, td {{\\n\"\n",
    "            f\"            border: 1px solid black;\\n\"\n",
    "            f\"            border-collapse: collapse;\\n\"\n",
    "            f\"        }}\\n\"\n",
    "            f\"        th, td {{\\n\"\n",
    "            f\"            padding: 8px;\\n\"\n",
    "            f\"        }}\\n\"\n",
    "            f\"        th {{\\n\"\n",
    "            f\"            background-color: #f2f2f2;\\n\"\n",
    "            f\"        }}\\n\"\n",
    "            f\"    </style>\\n\"\n",
    "            f\"</head>\\n<body>\\n\"\n",
    "            f\"{html_content}\\n\"\n",
    "            f\"</body>\\n</html>\"\n",
    "        )\n",
    "\n",
    "        with open(f\"weekly_report_{start_of_week.strftime('%Y%m%d')}_{end_of_week.strftime('%Y%m%d')}.md\", \"w\", encoding=\"utf-8\") as file_md:\n",
    "            file_md.write(email_base + markdown_report)\n",
    "\n",
    "        with open(f\"weekly_report_{start_of_week.strftime('%Y%m%d')}_{end_of_week.strftime('%Y%m%d')}.html\", \"w\", encoding=\"utf-8\") as file_html:\n",
    "            file_html.write(full_report_html)\n",
    "\n",
    "        print(\"md，html 报告生成成功。\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    generate_full_report()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "66e109e3-7b22-41e1-a3fc-9e4b538b9188",
   "metadata": {},
   "source": [
    "# 4、AtomGit 用户体验反馈排行\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "11a1287e-1c56-42b6-bb4d-00e103b537ca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "报告生成中，请稍候...\n",
      "md，html 报告生成成功。\n"
     ]
    }
   ],
   "source": [
    "import requests\n",
    "from datetime import datetime\n",
    "import pytz\n",
    "from collections import defaultdict\n",
    "import markdown\n",
    "\n",
    "# API 参数\n",
    "project_id = \"25287444\"\n",
    "token = \"your_token_here\"  # 替换为你的访问令牌\n",
    "leader_email = \"leader@atomgit.com\"  # 领导的邮箱\n",
    "\n",
    "# 获取 Issue 数据\n",
    "def get_issues(project_id, status, page_num=1, page_size=300):\n",
    "    url = f\"https://atomgit.com/api/issue/v1/page?projectId={project_id}&status={status}&order=recent_update&pageNum={page_num}&pageSize={page_size}\"\n",
    "    headers = {\n",
    "        'Accept': 'application/json, text/plain, */*',\n",
    "        \"Cookie\": f\"ATOMGIT_ID_TOKEN={token}\"\n",
    "    }\n",
    "    try:\n",
    "        response = requests.get(url, headers=headers)\n",
    "        response.raise_for_status()\n",
    "        return response.json()\n",
    "    except requests.exceptions.HTTPError as http_err:\n",
    "        print(f\"HTTP error occurred: {http_err}\")\n",
    "    except Exception as err:\n",
    "        print(f\"Other error occurred: {err}\")\n",
    "    return None\n",
    "\n",
    "# 获取所有 Issue 数据\n",
    "def get_all_issues(status=\"\"):\n",
    "    response = get_issues(project_id, status)\n",
    "    if response and 'content' in response:\n",
    "        return response['content']\n",
    "    else:\n",
    "        print(f\"Failed to fetch issues: {response['message'] if response else 'No response'}\")\n",
    "    return []\n",
    "\n",
    "# 获取 Issue 详细数据\n",
    "def get_issue_details(serial_number, project_id):\n",
    "    url = f\"https://atomgit.com/api/issue/v1/detail?serialNumber={serial_number}&projectId={project_id}\"\n",
    "    headers = {\n",
    "        'Accept': 'application/json, text/plain, */*',\n",
    "        \"Cookie\": f\"ATOMGIT_ID_TOKEN={token}\"\n",
    "    }\n",
    "    try:\n",
    "        response = requests.get(url, headers=headers)\n",
    "        response.raise_for_status()\n",
    "        return response.json()\n",
    "    except requests.exceptions.HTTPError as http_err:\n",
    "        print(f\"HTTP error occurred: {http_err}\")\n",
    "    except Exception as err:\n",
    "        print(f\"Other error occurred: {err}\")\n",
    "    return None\n",
    "\n",
    "# 生成排行榜数据\n",
    "def generate_leaderboard_data():\n",
    "    all_issues = get_all_issues(\"\")\n",
    "\n",
    "    start_of_year = datetime(2024, 1, 1, tzinfo=pytz.UTC)\n",
    "    start_of_half_year = datetime(2024, 7, 1, tzinfo=pytz.UTC)\n",
    "    issues_in_first_half = [issue for issue in all_issues if start_of_year <= datetime.fromisoformat(issue['createdDate'].replace(\"Z\", \"+00:00\")) < start_of_half_year]\n",
    "\n",
    "    issue_creators = defaultdict(list)\n",
    "    issue_commenters = defaultdict(set)\n",
    "\n",
    "    for issue in issues_in_first_half:\n",
    "        author_info = issue.get('authorInfo', {})\n",
    "        author_username = author_info.get('username', '未知')\n",
    "        issue_link = f\"[#{issue['serialNumber']}](https://atomgit.com/{issue['pathWithNamespace'].replace(' ', '')}/issues/{issue['serialNumber']})\"\n",
    "        issue_creators[author_username].append(issue_link)\n",
    "\n",
    "        serial_number = issue['serialNumber']\n",
    "        issue_details = get_issue_details(serial_number, project_id)\n",
    "        if issue_details and 'comments' in issue_details:\n",
    "            comments = issue_details['comments'].get('content', [])\n",
    "            for comment in comments:\n",
    "                commenter_info = comment.get('authorInfo', {})\n",
    "                commenter_username = commenter_info.get('username', '未知')\n",
    "                issue_commenters[commenter_username].add(issue_link)\n",
    "\n",
    "    sorted_creators = sorted(issue_creators.items(), key=lambda x: len(x[1]), reverse=True)\n",
    "    sorted_commenters = sorted(issue_commenters.items(), key=lambda x: len(x[1]), reverse=True)\n",
    "\n",
    "    return {\n",
    "        \"sorted_creators\": sorted_creators,\n",
    "        \"sorted_commenters\": sorted_commenters,\n",
    "    }\n",
    "\n",
    "# 生成周报内容 (Markdown 格式)\n",
    "def generate_report():\n",
    "    print(\"报告生成中，请稍候...\")\n",
    "    report_data = generate_leaderboard_data()\n",
    "    if report_data:\n",
    "        markdown_report_sections = []\n",
    "\n",
    "        # 标题\n",
    "        markdown_report_sections.append(\"# 2024年上半年 AtomGit 用户反馈贡献排行榜\")\n",
    "\n",
    "        # Issue 创建者排行榜\n",
    "        markdown_report_sections.append(\"\\n## Issue 创建者排行榜\")\n",
    "        markdown_report_sections.append(\"| 排名 | 用户名 | 创建 Issue 数 | 创建的 Issue 链接 |\")\n",
    "        markdown_report_sections.append(\"| --- | --- | --- | --- |\")\n",
    "        ranked_creators = report_data['sorted_creators']\n",
    "        for rank, (username, issues) in enumerate(ranked_creators, start=1):\n",
    "            issue_links = \", \".join(issues)\n",
    "            markdown_report_sections.append(f\"| {rank} | {username} | {len(issues)} | {issue_links} |\")\n",
    "\n",
    "        # Issue 互动者排行榜\n",
    "        markdown_report_sections.append(\"\\n## Issue 互动者排行榜\")\n",
    "        markdown_report_sections.append(\"| 排名 | 用户名 | 互动数 | 互动的 Issue 链接 |\")\n",
    "        markdown_report_sections.append(\"| --- | --- | --- | --- |\")\n",
    "        ranked_commenters = report_data['sorted_commenters']\n",
    "        for rank, (username, issues) in enumerate(ranked_commenters, start=1):\n",
    "            issue_links = \", \".join(issues)\n",
    "            markdown_report_sections.append(f\"| {rank} | {username} | {len(issues)} | {issue_links} |\")\n",
    "\n",
    "        markdown_report = \"\\n\".join(markdown_report_sections)\n",
    "\n",
    "        email_base = f\"\"\"\n",
    "> ----[start]------\n",
    "> subject: 2024年上半年用户反馈贡献排行榜\n",
    "> to: {leader_email}\n",
    "> ----[end]------\n",
    "\"\"\"\n",
    "\n",
    "        html_content = markdown.markdown(markdown_report, extensions=['extra', 'tables'])\n",
    "\n",
    "        full_report_html = (\n",
    "            \"<html>\\n<head>\\n\"\n",
    "            \"    <title>2024年上半年用户反馈贡献排行榜</title>\\n\"\n",
    "            \"    <style>\\n\"\n",
    "            \"        table, th, td {\\n\"\n",
    "            \"            border: 1px solid black;\\n\"\n",
    "            \"            border-collapse: collapse;\\n\"\n",
    "            \"        }\\n\"\n",
    "            \"        th, td {\\n\"\n",
    "            \"            padding: 8px;\\n\"\n",
    "            \"        }\\n\"\n",
    "            \"        th {\\n\"\n",
    "            \"            background-color: #f2f2f2;\\n\"\n",
    "            \"        }\\n\"\n",
    "            \"    </style>\\n\"\n",
    "            \"</head>\\n<body>\\n\"\n",
    "            + html_content +\n",
    "            \"\\n</body>\\n</html>\"\n",
    "        )\n",
    "\n",
    "        with open(f\"first_half_2024_leaderboard.md\", \"w\", encoding=\"utf-8\") as file_md:\n",
    "            file_md.write(email_base + markdown_report)\n",
    "\n",
    "        with open(f\"first_half_2024_leaderboard.html\", \"w\", encoding=\"utf-8\") as file_html:\n",
    "            file_html.write(full_report_html)\n",
    "\n",
    "        print(\"md，html 报告生成成功。\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    generate_report()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1c1b79e-af62-4c32-8be5-0d480d4339f5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
